//----------------------------------------------------------------
// vlog2Def
//----------------------------------------------------------------
// Convert from verilog netlist to a pre-placement DEF file
//----------------------------------------------------------------

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>	/* for getopt() */
#include <math.h>	/* For sqrt() and ceil() */

#include "hash.h"
#include "readverilog.h"
#include "readlef.h"

int write_output(struct cellrec *, int hasmacros, float aspect, float density,
		int units, GATE coresite, char *outname);
void helpmessage(FILE *outf);

/* Linked list for nets */

typedef struct _linkedNet *linkedNetPtr;

typedef struct _linkedNet {
    char *instname;
    char *pinname;
    linkedNetPtr next;
} linkedNet;

/* Hash table of LEF macros */
struct hashtable LEFhash;

/*--------------------------------------------------------------*/

int main (int argc, char *argv[])
{
    int i, result = 0, hasmacros = FALSE;
    int units = 100;
    float aspect = 1.0;
    float density = 1.0;
    struct cellrec *topcell;
    GATE coresite = NULL;
    char *defoutname = NULL;

    InitializeHashTable(&LEFhash, SMALLHASHSIZE);

    while ((i = getopt(argc, argv, "hHl:a:d:u:o:")) != EOF) {
        switch (i) {
	    case 'h':
	    case 'H':
		helpmessage(stdout);
		return 0;
	    case 'l':
		result = LefRead(optarg);	/* Can be called multiple times */
		if (result == 0) {
		    helpmessage(stderr);
		    return 1;
		}
		break;
	    case 'o':
		defoutname = strdup(optarg);
		break;
	    case 'a':
		if (sscanf(optarg, "%f", &aspect) != 1) {
		    fprintf(stderr, "Could not read aspect value from \"-a %s\"\n",
				optarg);
		    helpmessage(stderr);
		    return 1;
		}
		break;
	    case 'd':
		if (sscanf(optarg, "%f", &density) != 1) {
		    fprintf(stderr, "Could not read density value from \"-d %s\"\n",
				optarg);
		    helpmessage(stderr);
		    return 1;
		}
		if (density < 0.0 || density > 1.0) {
		    fprintf(stderr, "Illegal density value \"-d %s\"\n", optarg);
		    helpmessage(stderr);
		    return 1;
		}
		break;
	    case 'u':
		if (sscanf(optarg, "%d", &units) != 1) {
		    fprintf(stderr, "Could not read units value from \"-u %s\"\n",
				optarg);
		    helpmessage(stderr);
		    return 1;
		}
		break;
	    default:
		fprintf(stderr, "Bad option switch \"%c\"\n", (char)i);
		helpmessage(stderr);
		return 1;
   	}
    }

    if (optind >= argc) {
	fprintf(stderr, "Couldn't find a filename as input\n");
	helpmessage(stderr);
	return 1;
    }

    /* If any LEF files were read, hash the GateInfo list */
    if (GateInfo != NULL) {
	GATE gate;
	for (gate = GateInfo; gate; gate = gate->next) {
	    HashPtrInstall(gate->gatename, gate, &LEFhash);
	    if (!strncmp(gate->gatename, "site_", 5))
		if (gate->gateclass == MACRO_CLASS_CORE)
		    coresite = gate;
	}
	hasmacros = TRUE;
    }

    topcell = ReadVerilog(argv[optind]);
    result = write_output(topcell, hasmacros, aspect, density,
		units, coresite, defoutname);
    return result;
}

/*--------------------------------------------------------------*/
/* output_nets:							*/
/* Recursion callback function for each item in Nodehash	*/
/*--------------------------------------------------------------*/

struct nlist *output_nets(struct hashlist *p, void *cptr)
{
    struct netrec *net;
    char *sptr = NULL;
    FILE *outf = (FILE *)cptr;
    linkedNetPtr nlink, nsrch;

    nlink = (linkedNetPtr)(p->ptr);

    // Verilog backslash-escaped names are decidedly not SPICE
    // compatible, so replace the mandatory trailing space character
    // with another backslash.

    if (*p->name == '\\') {
	sptr = strchr(p->name, ' ');
	if (sptr != NULL) *sptr = '\\';
    }

    fprintf(outf, "- %s\n", p->name);

    for (nsrch = nlink; nsrch; nsrch = nsrch->next) {
	fprintf(outf, "  ( %s %s )", nsrch->instname, nsrch->pinname);
	if (nsrch->next == NULL)
	    fprintf(outf, " ;");
	fprintf(outf, "\n");
    }

    if (sptr != NULL) *sptr = ' ';
    return NULL;
}

/*--------------------------------------------------------------*/
/* port_output_specs						*/
/*								*/
/* Write information to an entry in the DEF PINS section	*/
/*--------------------------------------------------------------*/

void port_output_specs(FILE *outfptr, struct portrec *port,
	    LefList *routelayer, int units)
{
    char *layername;
    int x, y, ll, ur, w, hw;
    LefList pinlayer;

    /* This string array much match the port definitions in readverilog.h */
    static char *portdirs[] = {"", "INPUT", "OUTPUT", "INOUT"};

    x = y = 0;	    /* To be done (need constraints?) */

    /* To be done:  Layer depends on position and orientation */
    pinlayer = routelayer[2];

    layername = pinlayer->lefName;
    w = (int)(0.5 + pinlayer->info.route.width * (float)units);
    hw = w >> 1;
    ll = -hw;
    ur = w - hw;
    
    if (port->direction > PORT_NONE)
	fprintf(outfptr, "\n  + DIRECTION %s", portdirs[port->direction]);
    fprintf(outfptr, "\n  + LAYER %s ( %d %d ) ( %d %d )", layername,
		ll, ll, ur, ur);
    fprintf(outfptr, "\n  + PLACED ( %d %d ) N ;\n", x, y);
}

/*--------------------------------------------------------------*/
/* write_output							*/
/*								*/
/*         ARGS: 						*/
/*      RETURNS: 1 to OS					*/
/* SIDE EFFECTS: 						*/
/*--------------------------------------------------------------*/

int write_output(struct cellrec *topcell, int hasmacros, float aspect,
	float density, int units, GATE coresite, char *outname)
{
    FILE *outfptr = stdout;
    int ncomp, npin, nnet, start, end, i, result = 0;
    int totalwidth, totalheight, rowwidth, rowheight, numrows;
    int sitewidth, siteheight, numsites;
    char portnet[512];

    struct netrec *net;
    struct portrec *port;
    struct instance *inst;

    struct hashtable Nodehash;

    /* Static string "PIN" for ports */
    static char pinname[] = "PIN";

    linkedNetPtr nlink, nsrch;
    LefList slef;
    LefList routelayer[3];

    if (topcell == NULL) {
	fprintf(stderr, "No top-level cell data;  cannot continue.\n");
	return 1;
    }

    /* Open the output file (unless name is NULL, in which case use stdout) */
    if (outname != NULL) {
	outfptr = fopen(outname, "w");
	if (outfptr == NULL) {
	    fprintf(stderr, "Error:  Cannot open file %s for writing.\n", outname);
	    return 1;
	}
    } 

    /* Hash the nets */

    InitializeHashTable(&Nodehash, LARGEHASHSIZE);
    nnet = 0;
    for (port = topcell->portlist; port; port = port->next) {
	if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
	    start = net->start;
	    end = net->end;
	}
	else start = end = -1;

	if (start > end) {
	    int tmp;
	    tmp = start;
	    start = end;
	    end = tmp;
	}
	for (i = start; i <= end; i++) {
	    nlink = (linkedNetPtr)malloc(sizeof(linkedNet));
	    nlink->instname = pinname;

	    if (start == -1)
		nlink->pinname = port->name;
	    else {
		sprintf(portnet, "%s[%d]", port->name, i);
		nlink->pinname = strdup(portnet);
	    }
	    nlink->next = NULL;
	    if ((nsrch = HashLookup(nlink->pinname, &Nodehash)) != NULL) {
		while (nsrch->next) nsrch = nsrch->next;
		nsrch->next = nlink;
	    }
	    else {
		HashPtrInstall(nlink->pinname, nlink, &Nodehash);
		nnet++;
	    }
	}
    }
    totalwidth = 0;
    for (inst = topcell->instlist; inst; inst = inst->next) {
	int j;
	GATE gate;
	gate = HashLookup(inst->cellname, &LEFhash);

	for (port = inst->portlist; port; port = port->next) {

	    char *netsptr;

	    /* Code for handling vectors mostly copied from vlog2Cel.c. */
	    /* Note, however, that this program is given a synthesized	*/
	    /* netlist and there are no instance arrays.  If that is	*/
	    /* ever not true, then this code needs to be expanded to	*/
	    /* include handling of instance arrays.			*/

	    /* Determine and handle the four cases of a port and a net	*/
	    /* being an array or a single signal.			*/

	    int is_port_bus = FALSE;
	    int is_net_bus = FALSE;

            /* Verilog backslash-escaped names have spaces that     */
            /* break pretty much every other format, so replace     */
            /* the space with the (much more sensible) second       */
            /* backslash.  This can be detected and changed         */
            /* back by programs converting the syntax back into     */
            /* verilog.                                             */

	    netsptr = port->net;
	    if (*port->net == '\\') {
	        netsptr = strchr(port->net, ' ');
	        if (netsptr != NULL) *netsptr = '\\';
	    }

            /* Find the port name in the gate pin list */
            for (j = 0; j < gate->nodes; j++) {
                if (!strcmp(port->name, gate->node[j])) break;
            }
            if (j == gate->nodes) {
                /* Is this a bus? */
                for (j = 0; j < gate->nodes; j++) {
                    char *delim = strrchr(gate->node[j], '[');
                    if (delim != NULL) {
                        *delim = '\0';
                        if (!strcmp(port->name, gate->node[j]))
                            is_port_bus = TRUE;
                        *delim = '[';
                        if (is_port_bus) break;
                    }
                }
            }

            /* Check if the net itself is an array */
            net = HashLookup(port->net, &topcell->nets);
            if (net && (net->start != -1)) {
                char *sptr, *dptr, *cptr;

                is_net_bus = TRUE;

                /* However, if net name is a 1-bit bus subnet, then */
                /* it is not considered to be a bus.  Note that     */
                /* brackets inside a verilog backslash-escaped name */
                /* are not array indicators.                        */

                dptr = strchr(netsptr, '[');
                if (dptr) {
                    cptr = strchr(dptr + 1, ':');
                    if (!cptr) {
                        is_net_bus = FALSE;
                    }
                }
            }
	    else if (!net && port->net[0] == '{') {
		/* net is a signal bundle */
		is_net_bus = TRUE;
	    }

            if (j == gate->nodes) {
                fprintf(stderr, "Error:  Pin \"%s\" not found in LEF macro \"%s\"!\n",
                        port->name, gate->gatename);
            }
            else if (is_net_bus == FALSE) {

		nlink = (linkedNetPtr)malloc(sizeof(linkedNet));
		nlink->instname = inst->instname;
		nlink->pinname = port->name;
		nlink->next = NULL;

		if ((nsrch = HashLookup(port->net, &Nodehash)) != NULL) {
		    while (nsrch->next) nsrch = nsrch->next;
		    nsrch->next = nlink;
		}
		else {
		    HashPtrInstall(port->net, nlink, &Nodehash);
		    nnet++;
		}
	    }
            else {  // is_net_bus == TRUE

		char *apin, *anet, *dptr, *cptr;
		int a, pidx, armax, armin;

		armax = armin = 0;
		for (j = 0; j < gate->nodes; j++) {
		    char *delim, *sptr;

		    sptr = gate->node[j];
		    if (*sptr == '\\') sptr = strchr(sptr, ' ');
		    if (sptr == NULL) sptr = gate->node[j];
		    delim = strrchr(sptr, '[');
		    if (delim != NULL) {
			*delim = '\0';
			if (!strcmp(port->name, gate->node[j])) {
			    if (sscanf(delim + 1, "%d", &pidx) == 1) {
				if (pidx > armax) armax = pidx;
				if (pidx < armin) armin = pidx;
			    }
			}
			*delim = '[';
		    }
		}

		/* To do:  Need to check if array is high-to-low or low-to-high */
		/* Presently assuming arrays are always defined high-to-low	*/

		apin = (char *)malloc(strlen(port->name) + 15);
		for (a = armax; a >= armin; a--) {
		    if (is_port_bus)
			sprintf(apin, "%s[%d]", port->name, a);
		    else
			sprintf(apin, "%s", port->name);

		    /* If net is not delimited by {...} then it is also	*/
		    /* an array.  Otherwise, find the nth element in	*/
		    /* the brace-enclosed set.				*/

		    /* To do: if any component of the array is a vector	*/
		    /* then we need to count bits in that vector.	*/

		    if (*port->net == '{') {
			int aidx;
			char *sptr, ssave;
			char *pptr = port->net + 1;
			for (aidx = 0; aidx < (armax - a); aidx++) {
			    sptr = pptr;
			    while (*sptr != ',' && *sptr != '}') sptr++;
			    pptr = sptr + 1;
			}
			sptr = pptr;
			if (*sptr != '\0') {
			    while (*sptr != ',' && *sptr != '}') sptr++;
			    ssave = *sptr;
			    *sptr = '\0';
			    anet = (char *)malloc(strlen(pptr) + 1);
			    sprintf(anet, "%s", pptr);
			    *sptr = ssave;
			}
			else {
			    anet = NULL;	/* Must handle this error! */
			}
		    }
		    else if (((dptr = strrchr(netsptr, '[')) != NULL) &&
				((cptr = strrchr(netsptr, ':')) != NULL)) {
			int fhigh, flow, fidx;
			sscanf(dptr + 1, "%d", &fhigh);
			sscanf(cptr + 1, "%d", &flow);
			if (fhigh > flow) fidx = fhigh - (armax - a);
			else fidx = flow + (armax - a);
			anet = (char *)malloc(strlen(port->net) + 15);
			*dptr = '\0';
			sprintf(anet, "%s[%d]", port->net, fidx);
			*dptr = '[';
		    }
		    else {
			anet = (char *)malloc(strlen(port->net) + 15);
			sprintf(anet, "%s[%d]", port->net, a);
		    }

		    /* Find the corresponding port bit */
		    for (j = 0; j < gate->nodes; j++) {
			if (anet == NULL) break;
			if (!strcmp(apin, gate->node[j])) {

			    nlink = (linkedNetPtr)malloc(sizeof(linkedNet));
			    nlink->instname = inst->instname;
			    nlink->pinname = gate->node[j];
			    nlink->next = NULL;

			    if ((nsrch = HashLookup(anet, &Nodehash)) != NULL) {
				while (nsrch->next) nsrch = nsrch->next;
				nsrch->next = nlink;
			    }
			    else {
				HashPtrInstall(anet, nlink, &Nodehash);
				nnet++;
			    }
			    break;
			}
		    }
		    free(anet);
		    if (j == gate->nodes) {
			fprintf(stderr, "Error:  Failed to find port %s in cell %s"
				    " port list!\n", port->name, inst->cellname);
		    }
		}
		free(apin);
	    }
	}

	if (hasmacros) {
	    if (gate) {
		/* Make sure this is a core cell */
		if (gate->gateclass == MACRO_CLASS_CORE) {
		    totalwidth += (int)(gate->width * (float)units);
		    rowheight = (int)(gate->height * (float)units);
		}
		/* To do:  Handle non-core cell records */
		/* (specifically PAD and BLOCK).	*/
	    }
	}
    }

    /* For pin placement, find the 2nd and 3rd route layer LEF names.	*/
    /* NOTE:  This only ensures that the output is valid LEF;  it does	*/
    /* not do anything about applying pin constraints.			*/

    for (i = 0; i < 3; i++)
	routelayer[i] = (LefList)NULL;

    for (slef = LefInfo; slef; slef = slef->next)
        if (slef->lefClass == CLASS_ROUTE)
	    if ((slef->type < 3) && (slef->type >= 0))
		routelayer[slef->type] = slef;

    /* Write output DEF header */
    fprintf(outfptr, "VERSION 5.6 ;\n");
    /* fprintf(outfptr, "NAMESCASESENSITIVE ON  ;\n"); */
    fprintf(outfptr, "DIVIDERCHAR \"/\" ;\n");
    fprintf(outfptr, "BUSBITCHARS \"[]\" ;\n");
    fprintf(outfptr, "DESIGN %s ;\n", topcell->name);
    fprintf(outfptr, "UNITS DISTANCE MICRONS %d ;\n", units);
    fprintf(outfptr, "\n");

    /* Calculate pre-placement die area, rows, and tracks, and output the same,	*/
    /* depending on what has been read in from LEF files.			*/

    if (hasmacros) {
	/* NOTE:  Use a prorated density that is slightly lower than the target	*/
	/* or else the placement can fail due to fewer sites available then	*/
	/* cell area to place, after accounting for density.			*/

	int efftotalwidth = (int)ceilf((float)totalwidth / (density * 0.95));

	numrows = (int)ceilf(sqrtf(efftotalwidth / (aspect * rowheight)));
	rowwidth = (int)ceilf(efftotalwidth / numrows);
	totalheight = (int)ceilf(rowheight * numrows);
	sitewidth = (int)ceilf(coresite->width * units);
	siteheight = (int)ceilf(coresite->height * units);

	/* Diagnostic */
	fprintf(stdout, "Diagnostic:\n");
	fprintf(stdout, "Total width of all cells = %gum\n", (float)totalwidth / (float)units);
	fprintf(stdout, "Effective total width after density planning = %gum\n", (float)efftotalwidth / (float)units);
	fprintf(stdout, "Site size = (%gum, %gum)\n", (float)sitewidth / (float)units, siteheight / (float)units);
	fprintf(stdout, "Row height = %gum\n", (float)rowheight / (float)units);
	fprintf(stdout, "Row width = %gum\n", (float)rowwidth / (float)units);
	fprintf(stdout, "Total height = %gum\n", (float)totalheight / (float)units);

	/* To do: compute additional area for pins */

	fprintf(outfptr, "DIEAREA ( 0 0 ) ( %d %d ) ;\n", rowwidth, totalheight);
	fprintf(outfptr, "\n");

        /* Compute site placement and generate ROW statements */

	numsites = (int)ceilf((float)rowwidth / (float)sitewidth);
	for (i = 0; i < numrows; i++) {
	    fprintf(outfptr, "ROW ROW_%d %s 0 %d %c DO %d BY 1 STEP %d 0 ;\n",
			i + 1, coresite->gatename + 5, i * siteheight,
			((i % 2) == 0) ? 'N' : 'S', numsites, sitewidth);
	}
	fprintf(outfptr, "\n");
    }

    /* Write components in the order of the input file */

    ncomp = 0;
    for (inst = topcell->instlist; inst; inst = inst->next) {
	ncomp++;
        if (inst->arraystart != -1) {
	    int arrayw = inst->arraystart - inst->arrayend;
	    ncomp += (arrayw < 0) ? -arrayw : arrayw;
	}
    }
    fprintf(outfptr, "COMPONENTS %d ;\n", ncomp);

    for (inst = topcell->instlist; inst; inst = inst->next) {
	if (inst->arraystart != -1) {
	    int ahigh, alow, j;
	    if (inst->arraystart > inst->arrayend) {
		ahigh = inst->arraystart;
		alow = inst->arrayend;
	    }
	    else {
		ahigh = inst->arrayend;
		alow = inst->arraystart;
	    }
	    for (j = ahigh; j >= alow; j--) {
		fprintf(outfptr, "- %s[%d] %s ;\n", inst->instname, j, inst->cellname);
	    }
	}
	else
	    fprintf(outfptr, "- %s %s ;\n", inst->instname, inst->cellname);
    }
    fprintf(outfptr, "END COMPONENTS\n\n");

    npin = 0;
    for (port = topcell->portlist; port; port = port->next) {
	if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
	    int btot = net->start - net->end;
	    if (btot < 0) btot = -btot;
	    npin += btot + 1;
	}
	else
	    npin++;
    }
    fprintf(outfptr, "PINS %d ;\n", npin);

    for (port = topcell->portlist; port; port = port->next) {
	if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
	    if (net->start == -1) {
		fprintf(outfptr, "- %s + NET %s", port->name, port->name);
		port_output_specs(outfptr, port, routelayer, units);
	    }
	    else if (net->start > net->end) {
		for (i = net->start; i >= net->end; i--) {
		    fprintf(outfptr, "- %s[%d] + NET %s[%d]", port->name, i,
				port->name, i);
		    port_output_specs(outfptr, port, routelayer, units);
		}
	    }
	    else {
		for (i = net->start; i <= net->end; i++) {
		    fprintf(outfptr, "- %s[%d] + NET %s[%d]", port->name, i,
				port->name, i);
		    port_output_specs(outfptr, port, routelayer, units);
		}
	    }
	}
	else {
	    fprintf(outfptr, "- %s + NET %s", port->name, port->name);
	    port_output_specs(outfptr, port, routelayer, units);
	}
    }

    fprintf(outfptr, "END PINS\n\n");

    fprintf(outfptr, "NETS %d ;\n", nnet);
    RecurseHashTablePointer(&Nodehash, output_nets, outfptr);
    fprintf(outfptr, "END NETS\n\n");

    /* End the design */
    fprintf(outfptr, "END DESIGN\n");

    if (outname != NULL) fclose(outfptr);

    fflush(stdout);
    return result;

} /* write_output */

/*--------------------------------------------------------------*/
/* C helpmessage - tell user how to use the program		*/
/*								*/
/*         ARGS: 						*/
/*      RETURNS: 1 to OS					*/
/* SIDE EFFECTS: 						*/
/*--------------------------------------------------------------*/

void helpmessage(FILE *outf)
{
    fprintf(outf,"vlog2Def <netlist>\n");
    fprintf(outf,"\n");
    fprintf(outf,"vlog2Def converts a verilog netlist to a pre-placement DEF file.\n");
    fprintf(outf,"\n");
    fprintf(outf,"options:\n");
    fprintf(outf,"\n");
    fprintf(outf,"   -h          Print this message\n");
    fprintf(outf,"   -o <path>   Set output filename (otherwise output is on stdout).\n");
    fprintf(outf,"   -l <path>   Read LEF file from <path> (may be called multiple"
			" times)\n");
    fprintf(outf,"   -a <value>	 Set aspect ratio to <value> (default 1.0)\n");
    fprintf(outf,"   -d <value>	 Set density to <value> (default 1.0)\n");
    fprintf(outf,"   -u <value>  Set units-per-micron to <value) (default 100)\n");

} /* helpmessage() */

